using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace GP2D3D_Homework5_RickBeijer_S1012261
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        private GraphicsDeviceManager graphicsDeviceManager;
        private SpriteBatch spriteBatch;
        private bool isKeyEscapeDown;
        private bool isKeyNumpadZeroDown;
        private bool isKeyOneDown;
        private bool isKeyTwoDown;
        private bool isKeyThreeDown;
        private bool isKeyFourDown;
        private bool isKeyFiveDown;
        private bool isKeySixDown;
        private bool isNumberedKeyDown;
        private Keys numberedKeyPressed;
        private Keys directionalKeyPressed;
        private BasicEffect basicEffect;
        private Model simpleCube;
        private List<Matrix> firstTranslations;
        private List<Matrix> secondRotations;
        private List<int> left;
        private List<int> right;
        private List<int> top;
        private List<int> bottom;
        private List<int> back;
        private List<int> front;
        private Matrix secondTranslation;
        private Texture2D texture;
        private Vector3 cameraPosition;
        private Matrix cameraRotationX;
        private Matrix cameraRotationY;
        private bool isAnimating;
        private int animationStep;

        public Game1()
        {
            this.graphicsDeviceManager = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            IsMouseVisible = true;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            cameraPosition = new Vector3( 0, 0, 10 );
            this.basicEffect = new BasicEffect( this.GraphicsDevice, null );
            this.basicEffect.World = Matrix.Identity;
            this.basicEffect.View = Matrix.CreateLookAt( cameraPosition, Vector3.Zero, Vector3.Up );
            this.basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView( MathHelper.ToRadians( 45 ),
                                                                               (float) this.Window.ClientBounds.Width /
                                                                               (float) this.Window.ClientBounds.Height,
                                                                               0.1f,
                                                                               100f );

            firstTranslations = new List<Matrix>();
            secondRotations = new List<Matrix>();
            secondTranslation = Matrix.CreateTranslation( new Vector3( 0.5f, 0, 0.5f ) );

            float halfCubeSize = 1.5f;
            for ( float z = 0; z < 3; z++ )
            {
                for ( float y = 0; y < 3; y++ )
                {
                    for ( float x = 0; x < 3; x++ )
                    {
                        this.secondRotations.Add( Matrix.Identity );

                        this.firstTranslations.Add( Matrix.CreateTranslation( new Vector3( x - halfCubeSize, y - halfCubeSize, z - halfCubeSize ) ) );
                    }
                }
            }

            cameraRotationX = Matrix.Identity;
            cameraRotationY = Matrix.Identity;

            left = new List<int>() { 0, 3, 6, 9, 12, 15, 18, 21, 24 };
            right = new List<int>() { 2, 5, 8, 11, 14, 17, 20, 23, 26 };
            top = new List<int>() { 6, 7, 8, 15, 16, 17, 24, 25, 26 };
            bottom = new List<int>() { 0, 1, 2, 9, 10, 11, 18, 19, 20 };
            back = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
            front = new List<int>() { 18, 19, 20, 21, 22, 23, 24, 25, 26 };

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            simpleCube = Content.Load<Model>("cubesimple");
            texture = Content.Load<Texture2D>( "cubeTexture" );
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            #region Animation
            if (isAnimating)
            {
                float rotation = -1.875f;
                
                switch ( directionalKeyPressed )
                {
                    case Keys.Left:
                        rotation = -rotation;
                        break;
                }

                switch (numberedKeyPressed)
                {
                    case Keys.D1:
                        foreach ( int index in left )
                        {
                            secondRotations[index] *= Matrix.CreateRotationX( MathHelper.ToRadians( -rotation ) );
                        }
                        break;
                    case Keys.D2:
                        foreach ( int index in right )
                        {
                            secondRotations[index] *= Matrix.CreateRotationX(MathHelper.ToRadians(-rotation));
                        }
                        break;
                    case Keys.D3:
                        foreach ( int index in top )
                        {
                            secondRotations[index] *= Matrix.CreateRotationY(MathHelper.ToRadians(rotation));
                        }
                        break;
                    case Keys.D4:
                        foreach ( int index in bottom )
                        {
                            secondRotations[index] *= Matrix.CreateRotationY(MathHelper.ToRadians(rotation));
                        }
                        break;
                    case Keys.D5:
                        foreach ( int index in back )
                        {
                            secondRotations[index] *= Matrix.CreateRotationZ(MathHelper.ToRadians(rotation));
                        }
                        break;
                    case Keys.D6:
                        foreach ( int index in front )
                        {
                            secondRotations[index] *= Matrix.CreateRotationZ(MathHelper.ToRadians(rotation));
                        }
                        break;
                }

                animationStep++;

                if (animationStep > 47)
                {
                    isAnimating = false;
                }
            }
            #endregion

            #region Keys
            // Allows the game to exit
            if ( Keyboard.GetState().IsKeyUp( Keys.Escape ) && isKeyEscapeDown )
            {
                this.Exit();
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Up))
            {
                cameraRotationX *= Matrix.CreateRotationX( MathHelper.ToRadians( -1.875f ) );
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Down))
            {
                cameraRotationX *= Matrix.CreateRotationX( MathHelper.ToRadians( 1.875f ) );
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Left))
            {
                if ( !isAnimating && isNumberedKeyDown )
                {
                    isAnimating = true;
                    animationStep = 0;
                    directionalKeyPressed = Keys.Left;

                    #region Numbered Keys
                    if (isKeyOneDown)
                    {
                        numberedKeyPressed = Keys.D1;

                        int two = left[2];
                        int seven = left[7];

                        left[7] = left[3];
                        left[3] = left[1];
                        left[1] = left[5];
                        left[5] = seven;
                        left[2] = left[8];
                        left[8] = left[6];
                        left[6] = left[0];
                        left[0] = two;

                        top[0] = left[2]; top[3] = left[5]; top[6] = left[8];
                        bottom[0] = left[0]; bottom[3] = left[3]; bottom[6] = left[6];
                        back[0] = left[0]; back[3] = left[1]; back[6] = left[2];
                        front[0] = left[6]; front[3] = left[7]; front[6] = left[8];
                    }
                    if ( isKeyTwoDown )
                    {
                        numberedKeyPressed = Keys.D2;

                        int two = right[2];
                        int seven = right[7];

                        right[7] = right[3];
                        right[3] = right[1];
                        right[1] = right[5];
                        right[5] = seven;
                        right[2] = right[8];
                        right[8] = right[6];
                        right[6] = right[0];
                        right[0] = two;

                        top[2] = right[2]; top[5] = right[5]; top[8] = right[8];
                        bottom[2] = right[0]; bottom[5] = right[3]; bottom[8] = right[6];
                        back[2] = right[0]; back[5] = right[1]; back[8] = right[2];
                        front[2] = right[6]; front[5] = right[7]; front[8] = right[8];
                    }
                    if ( isKeyThreeDown )
                    {
                        numberedKeyPressed = Keys.D3;

                        int two = top[2];
                        int seven = top[7];

                        top[7] = top[3];
                        top[3] = top[1];
                        top[1] = top[5];
                        top[5] = seven;
                        top[2] = top[8];
                        top[8] = top[6];
                        top[6] = top[0];
                        top[0] = two;

                        left[2] = top[0]; left[5] = top[3]; left[8] = top[6];
                        right[2] = top[2]; right[5] = top[5]; right[8] = top[8];
                        back[6] = top[0]; back[7] = top[1]; back[8] = top[2];
                        front[6] = top[6]; front[7] = top[7]; front[8] = top[8];
                    }
                    if ( isKeyFourDown )
                    {
                        numberedKeyPressed = Keys.D4;

                        int two = bottom[2];
                        int seven = bottom[7];

                        bottom[7] = bottom[3];
                        bottom[3] = bottom[1];
                        bottom[1] = bottom[5];
                        bottom[5] = seven;
                        bottom[2] = bottom[8];
                        bottom[8] = bottom[6];
                        bottom[6] = bottom[0];
                        bottom[0] = two;

                        left[0] = bottom[0]; left[3] = bottom[3]; left[6] = bottom[6];
                        right[0] = bottom[2]; right[3] = bottom[5]; right[6] = bottom[8];
                        back[0] = bottom[0]; back[1] = bottom[1]; back[2] = bottom[2];
                        front[0] = bottom[6]; front[1] = bottom[7]; front[2] = bottom[8];
                    }
                    if ( isKeyFiveDown )
                    {
                        numberedKeyPressed = Keys.D5;

                        int two = back[2];
                        int seven = back[7];

                        back[2] = back[0];
                        back[0] = back[6];
                        back[7] = back[5];
                        back[5] = back[1];
                        back[1] = back[3];
                        back[3] = seven;
                        back[6] = back[8];
                        back[8] = two;

                        left[0] = back[0]; left[1] = back[3]; left[2] = back[6];
                        right[0] = back[2]; right[1] = back[5]; right[2] = back[8];
                        top[0] = back[6]; top[1] = back[7]; top[2] = back[8];
                        bottom[0] = back[0]; bottom[1] = back[1]; bottom[2] = back[2];
                    }
                    if ( isKeySixDown )
                    {
                        numberedKeyPressed = Keys.D6;

                        int two = front[2];
                        int seven = front[7];

                        front[2] = front[0];
                        front[0] = front[6];
                        front[7] = front[5];
                        front[5] = front[1];
                        front[1] = front[3];
                        front[3] = seven;
                        front[6] = front[8];
                        front[8] = two;

                        left[6] = front[0]; left[7] = front[3]; left[8] = front[6];
                        right[6] = front[2]; right[7] = front[5]; right[8] = front[8];
                        top[6] = front[6]; top[7] = front[7]; top[8] = front[8];
                        bottom[6] = front[0]; bottom[7] = front[1]; bottom[8] = front[2];
                    }
                    #endregion
                }
                else if (!isNumberedKeyDown)
                {
                    cameraRotationY *= Matrix.CreateRotationY( MathHelper.ToRadians( -1.875f ) );
                }
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Right))
            {
                if ( !isAnimating && isNumberedKeyDown )
                {
                    isAnimating = true;
                    animationStep = 0;
                    directionalKeyPressed = Keys.Right;

                    #region Numbered Keys
                    if (isKeyOneDown)
                    {
                        numberedKeyPressed = Keys.D1;

                        int two = left[2];
                        int seven = left[7];

                        left[2] = left[0];
                        left[0] = left[6];
                        left[7] = left[5];
                        left[5] = left[1];
                        left[1] = left[3];
                        left[3] = seven;
                        left[6] = left[8];
                        left[8] = two;

                        top[0] = left[2]; top[3] = left[5]; top[6] = left[8];
                        bottom[0] = left[0]; bottom[3] = left[3]; bottom[6] = left[6];
                        back[0] = left[0]; back[3] = left[1]; back[6] = left[2];
                        front[0] = left[6]; front[3] = left[7]; front[6] = left[8];
                    }
                    if (isKeyTwoDown)
                    {
                        numberedKeyPressed = Keys.D2;

                        int two = right[2];
                        int seven = right[7];

                        right[2] = right[0];
                        right[0] = right[6];
                        right[7] = right[5];
                        right[5] = right[1];
                        right[1] = right[3];
                        right[3] = seven;
                        right[6] = right[8];
                        right[8] = two;

                        top[2] = right[2]; top[5] = right[5]; top[8] = right[8];
                        bottom[2] = right[0]; bottom[5] = right[3]; bottom[8] = right[6];
                        back[2] = right[0]; back[5] = right[1]; back[8] = right[2];
                        front[2] = right[6]; front[5] = right[7]; front[8] = right[8];
                    }
                    if (isKeyThreeDown)
                    {
                        numberedKeyPressed = Keys.D3;

                        int two = top[2];
                        int seven = top[7];

                        top[2] = top[0];
                        top[0] = top[6];
                        top[7] = top[5];
                        top[5] = top[1];
                        top[1] = top[3];
                        top[3] = seven;
                        top[6] = top[8];
                        top[8] = two;

                        left[2] = top[0]; left[5] = top[3]; left[8] = top[6];
                        right[2] = top[2]; right[5] = top[5]; right[8] = top[8];
                        back[6] = top[0]; back[7] = top[1]; back[8] = top[2];
                        front[6] = top[6]; front[7] = top[7]; front[8] = top[8];
                    }
                    if (isKeyFourDown)
                    {
                        numberedKeyPressed = Keys.D4;

                        int two = bottom[2];
                        int seven = bottom[7];

                        bottom[2] = bottom[0];
                        bottom[0] = bottom[6];
                        bottom[7] = bottom[5];
                        bottom[5] = bottom[1];
                        bottom[1] = bottom[3];
                        bottom[3] = seven;
                        bottom[6] = bottom[8];
                        bottom[8] = two;

                        left[0] = bottom[0]; left[3] = bottom[3]; left[6] = bottom[6];
                        right[0] = bottom[2]; right[3] = bottom[5]; right[6] = bottom[8];
                        back[0] = bottom[0]; back[1] = bottom[1]; back[2] = bottom[2];
                        front[0] = bottom[6]; front[1] = bottom[7]; front[2] = bottom[8];
                    }
                    if (isKeyFiveDown)
                    {
                        numberedKeyPressed = Keys.D5;

                        int two = back[2];
                        int seven = back[7];

                        back[7] = back[3];
                        back[3] = back[1];
                        back[1] = back[5];
                        back[5] = seven;
                        back[2] = back[8];
                        back[8] = back[6];
                        back[6] = back[0];
                        back[0] = two;

                        left[0] = back[0]; left[1] = back[3]; left[2] = back[6];
                        right[0] = back[2]; right[1] = back[5]; right[2] = back[8];
                        top[0] = back[6]; top[1] = back[7]; top[2] = back[8];
                        bottom[0] = back[0]; bottom[1] = back[1]; bottom[2] = back[2];
                    }
                    if (isKeySixDown)
                    {
                        numberedKeyPressed = Keys.D6;

                        int two = front[2];
                        int seven = front[7];

                        front[7] = front[3];
                        front[3] = front[1];
                        front[1] = front[5];
                        front[5] = seven;
                        front[2] = front[8];
                        front[8] = front[6];
                        front[6] = front[0];
                        front[0] = two;

                        left[6] = front[0]; left[7] = front[3]; left[8] = front[6];
                        right[6] = front[2]; right[7] = front[5]; right[8] = front[8];
                        top[6] = front[6]; top[7] = front[7]; top[8] = front[8];
                        bottom[6] = front[0]; bottom[7] = front[1]; bottom[8] = front[2];
                    }
                    #endregion
                }
                else if ( !isNumberedKeyDown )
                {
                    cameraRotationY *= Matrix.CreateRotationY( MathHelper.ToRadians( 1.875f ) );
                }
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Add))
            {
                cameraPosition.Z -= 0.25f;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Subtract))
            {
                cameraPosition.Z += 0.25f;
            }
            if ( Keyboard.GetState().IsKeyUp( Keys.NumPad0 ) && isKeyNumpadZeroDown )
            {
                cameraRotationX = Matrix.Identity;
                cameraRotationY = Matrix.Identity;
                cameraPosition.Z = 10;
            }

            isKeyEscapeDown = Keyboard.GetState().IsKeyDown( Keys.Escape );
            isKeyNumpadZeroDown = Keyboard.GetState().IsKeyDown( Keys.NumPad0 );
            isKeyOneDown = Keyboard.GetState().IsKeyDown( Keys.D1 );
            isKeyTwoDown = Keyboard.GetState().IsKeyDown( Keys.D2 );
            isKeyThreeDown = Keyboard.GetState().IsKeyDown( Keys.D3 );
            isKeyFourDown = Keyboard.GetState().IsKeyDown( Keys.D4 );
            isKeyFiveDown = Keyboard.GetState().IsKeyDown( Keys.D5 );
            isKeySixDown = Keyboard.GetState().IsKeyDown( Keys.D6 );
            isNumberedKeyDown = isKeyOneDown || isKeyTwoDown || isKeyThreeDown || isKeyFourDown || isKeyFiveDown || isKeySixDown;
            #endregion

            this.basicEffect.View = Matrix.CreateLookAt( Vector3.Transform( cameraPosition, cameraRotationX * cameraRotationY ),
                                                         Vector3.Zero,
                                                         Vector3.Up );

            base.Update( gameTime );
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            int counter = 0;
            foreach (Matrix translation in firstTranslations)
            {
                foreach (ModelMesh cubeMesh in simpleCube.Meshes)
                {
                    foreach ( BasicEffect cubeEffect in cubeMesh.Effects )
                    {
                        cubeEffect.World = this.basicEffect.World *
                                           firstTranslations[counter] *
                                           secondTranslation *
                                           secondRotations[counter];
                        cubeEffect.View = this.basicEffect.View;
                        cubeEffect.Projection = this.basicEffect.Projection;

                        cubeEffect.TextureEnabled = true;
                        cubeEffect.Texture = texture;

                        cubeEffect.LightingEnabled = true;
                        cubeEffect.DirectionalLight0.Enabled = true;
                        cubeEffect.DirectionalLight0.DiffuseColor = new Vector3(1, 1, 1);
                        cubeEffect.DirectionalLight0.SpecularColor = new Vector3(2, 2, 2);
                        cubeEffect.DirectionalLight0.Direction = new Vector3(1, -1, -1);
                        cubeEffect.DirectionalLight1.Enabled = true;
                        cubeEffect.DirectionalLight1.DiffuseColor = new Vector3(1, 1, 1);
                        cubeEffect.DirectionalLight1.SpecularColor = new Vector3(2, 2, 2);
                        cubeEffect.DirectionalLight1.Direction = new Vector3(-1, 1, 1);
                    }

                    cubeMesh.Draw();
                }

                counter++;
            }

            base.Draw(gameTime);
        }
    }
}